package com.study.locks.redis;

import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;

import java.util.Collections;

/**
 * @author: chenhs
 * @date: Created in 6:12 2019/12/16
 **/
@Component
public class RedisDistributeLock {

    /**
     * 尝试获取分布式锁
     *
     * @param jedis      Redis客户端
     * @param key        锁
     * @param value      请求标识
     * @param expireTime 超时时间
     * @return 是否获取成功
     */
    public boolean tryGetDistributeLock(Jedis jedis, String key, String value, int expireTime) {
        try {
            String result = jedis.set(key, value, "NX", "PX", expireTime);
            if ("OK".equals(result)) {
                return true;
            }
            return false;
        } finally {
            JedisPoolUtil.closeJedis(jedis);
        }
    }


    private final Long RELEASE_SUCCESS = 1L;

    /**
     * 获取锁时的睡眠时间片，单位毫秒
     */
    private long SLEEP_PER = 5;

    private final String key = "lock_key";
    private final String value = "lock_value";

    public final int expireTimeInMilliseconds = 5 * 1000;


    /**
     * 释放分布式锁
     *
     * @param jedis   Redis客户端
     * @param lockKey 锁
     * @param value   请求标识
     * @return 是否释放成功
     */
    public boolean unlock(Jedis jedis, String lockKey, String value) {
        try {
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(value));
            if (RELEASE_SUCCESS.equals(result)) {
                return true;
            }
            return false;
        } finally {
            JedisPoolUtil.closeJedis(jedis);
        }
    }


    public void lock() {
        try (Jedis jedis = JedisPoolUtil.getJedis()) {
            while (!(tryGetDistributeLock(jedis, key, value, expireTimeInMilliseconds))) {
                try {
                    Thread.sleep(SLEEP_PER);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public boolean tryLock() {
        try (Jedis jedis = JedisPoolUtil.getJedis()) {
            return tryGetDistributeLock(jedis, key, value, expireTimeInMilliseconds);
        }
    }

    public void unlock() {
        try (Jedis jedis = JedisPoolUtil.getJedis()) {
            unlock(jedis, key, value);
        }
    }

}
